package org.myspringframework.core;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import java.io.InputStream;
import java.lang.reflect.Constructor;

public class ClassPathXmlApplicationContext implements ApplicationContext{

    private static final Logger logger = LoggerFactory.getLogger(ClassPathXmlApplicationContext.class);

    private Map<String,Object> singletonObjects = new HashMap<>();

    /*解析myspring的配置文件，然后初始化所有的Bean对象
    * configLocation spring配置文件的路径
    * 使用ClassPathXmlApplicationContext，配置文件应当放到类路径下*/

    public ClassPathXmlApplicationContext(String configLocation) {
        try {
            // 解析myspring.xml文件，然后实例化Bean,将Bean存放在singletonObjects集合当中
            SAXReader reader = new SAXReader();  // dom4j解析XML文件的核心对象
            // 下面的代码只能从类路径中加载，获取一个输入流，执行一个配置文件
            InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream(configLocation);
            Document document = reader.read(in);
            // 获取所有的Bean标签
            List<Node> nodes = document.selectNodes("//bean");
            // 遍历bean标签
            nodes.forEach(node -> {
                try {
                    // 处理反射对象出现的异常

                    // 为了验证是否获取到bean标签
                    // System.out.println(node);

                    // 向下转型的目的是为了使用Element接口里更加丰富的方法
                    Element beanElt = (Element) node;
                    // 获取id
                    String id = beanElt.attributeValue("id");
                    // 获取class
                    String className = beanElt.attributeValue("class");
                    // 利用日志进行记录
                    logger.info("beanName = "+id);
                    logger.info("beanClassName = "+className);
                    // 通过反射机制创建对象，将其放到Map集合中，提前曝光
                    // 获取class
                    Class<?> aClass = Class.forName(className);
                    // 获取无参数构造方法
                    Constructor<?> defaultCon = aClass.getDeclaredConstructor();
                    // 调用无参数构造方法实例化Bean
                    Object bean = defaultCon.newInstance();
                    // 将Bean曝光
                    singletonObjects.put(id,bean);
                    // 记录日志
                    logger.info(singletonObjects.toString());
                }catch (Exception e) {
                    e.printStackTrace();
                }
            });

            // 再次把所有的bean标签遍历一遍，这一次主要是给属性赋值
            nodes.forEach(node -> {
                try {
                    // 这个是为获取类名时抛出的异常

                    Element beanElt = (Element) node;
                    // 获取id
                    String id = beanElt.attributeValue("id");
                    // 获取className
                    String className = beanElt.attributeValue("class");

                    // 获取Class
                    Class<?> aClass = Class.forName(className);

                    // 获取该bean标签下所有的属性property标签
                    List<Element> properties = beanElt.elements("property");
                    // 遍历所有的属性标签
                    properties.forEach(property -> {
                        try {
                            // 获取属性名称抛出的异常

                            // 获取属性名
                            String propertyName = property.attributeValue("name");
                            logger.info("属性名" + propertyName);
                            // 由属性名称获取属性类型
                            Field field = aClass.getDeclaredField(propertyName);
                            // 获取set方法名
                            String setMethodName = "set" + propertyName.toUpperCase().charAt(0) + propertyName.substring(1);

                            // 获取set方法
                            Method setMethod = aClass.getDeclaredMethod(setMethodName,field.getType());
                            // 获取具体的值
                            String value = property.attributeValue("value");
                            String ref = property.attributeValue("ref");

//                            Object actualValue = null;       // 真值
//                            if(value != null) {
//                                // 值是简单类型
//                                // 调用set方法（set方法没有返回值）
//                                // 调用singletonObjects.get(id)对象，即name
//                                // 由于value是一个字符串，但是可以set方法传参不是字符串。。。
//
//                                // 只支持这些类型为简单类型
//                                // byte,short,int,long,float,double,boolean,char
//                                // Byte Short Integer Long Float Double Boolean Character String
//
//                                // 获取属性类型名
//                                // field --> private String name
//                                // field.getType() --> String
//                                // field.getType().getName --> "java.lang.String"
//                                // field.getType().getSimpleName --> "String"
//                                String propertyTypeSimpleName = field.getType().getSimpleName();
//                                switch (propertyTypeSimpleName) {
//                                    case "byte":
//                                        actualValue = Byte.parseByte(value);
//                                        break;
//                                    case "short":
//                                        actualValue = Short.parseShort(value);
//                                        break;
//                                    case "int":
//                                        actualValue = Integer.parseInt(value);
//                                        break;
//                                    case "long":
//                                        actualValue = Long.parseLong(value);
//                                        break;
//                                    case "float":
//                                        actualValue = Float.parseFloat(value);
//                                        break;
//                                    case "double":
//                                        actualValue = Double.parseDouble(value);
//                                        break;
//                                    case "boolean":
//                                        actualValue = Boolean.parseBoolean(value);
//                                        break;
//                                    case "char":
//                                        actualValue = value.charAt(0);
//                                        break;
//                                    case "Byte":
//                                        actualValue = Byte.valueOf(value);
//                                        break;
//                                    case "Short":
//                                        actualValue = Short.valueOf(value);
//                                        break;
//                                    case "Integer":
//                                        actualValue = Integer.valueOf(value);
//                                        break;
//                                    case "Long":
//                                        actualValue = Long.valueOf(value);
//                                        break;
//                                    case "Float":
//                                        actualValue = Float.valueOf(value);
//                                        break;
//                                    case "Double":
//                                        actualValue = Double.valueOf(value);
//                                        break;
//                                    case "Boolean":
//                                        actualValue = Boolean.valueOf(value);
//                                        break;
//                                    case "Character":
//                                        actualValue = Character.valueOf(value.charAt(0));
//                                        break;
//                                    case "String":
//                                        actualValue = value;
//                                        break;
//                                }
//                                setMethod.invoke(singletonObjects.get(id),actualValue);
//                            }

                            if (value != null) {
                                // 值是简单类型
                                // 调用set方法（set方法没有返回值）
                                // 调用singletonObjects.get(id)对象，即name
                                // 获取属性类型名
                                String propertyTypeSimpleName = field.getType().getSimpleName();

                                // 创建一个映射，将类型名称映射到转换函数
                                Map<String, Function<String, ?>> typeConverterMap = new HashMap<>();
                                typeConverterMap.put("byte", Byte::parseByte);
                                typeConverterMap.put("short", Short::parseShort);
                                typeConverterMap.put("int", Integer::parseInt);
                                typeConverterMap.put("long", Long::parseLong);
                                typeConverterMap.put("float", Float::parseFloat);
                                typeConverterMap.put("double", Double::parseDouble);
                                typeConverterMap.put("boolean", Boolean::parseBoolean);
                                typeConverterMap.put("char", s -> s.charAt(0));
                                typeConverterMap.put("Byte", Byte::valueOf);
                                typeConverterMap.put("Short", Short::valueOf);
                                typeConverterMap.put("Integer", Integer::valueOf);
                                typeConverterMap.put("Long", Long::valueOf);
                                typeConverterMap.put("Float", Float::valueOf);
                                typeConverterMap.put("Double", Double::valueOf);
                                typeConverterMap.put("Boolean", Boolean::valueOf);
                                typeConverterMap.put("Character", s -> Character.valueOf(s.charAt(0)));
                                typeConverterMap.put("String", s -> s);

                                // 使用映射来获取转换函数并转换值
                                Function<String, ?> converter = typeConverterMap.get(propertyTypeSimpleName);
                                if (converter != null) {
                                    Object actualValue = converter.apply(value);
                                    setMethod.invoke(singletonObjects.get(id), actualValue);
                                } else {
                                    // 处理未知类型
                                    logger.error("不支持的类型: " + propertyTypeSimpleName);
                                }
                            }

                            if(ref!=null) {
                                // 值是非简单类型
                                // 调用的是ref，ref是id,有了id，可以获取class对象，获取到对象，就可以赋到name属性上
                                // 因此具体的值就是那个对象
                                setMethod.invoke(singletonObjects.get(id),singletonObjects.get(ref));
                            }

                        }catch (Exception e){
                            e.printStackTrace();
                        }

                    });
                }catch (Exception e) {
                    e.printStackTrace();
                }
            });

        }catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public Object getBean(String beanName) {
        return singletonObjects.get(beanName);
    }
}
